package cn.cas.mango.service;

import cn.cas.mango.config.Mapping;
import cn.cas.mango.dto.param.GlobalParam;
import cn.cas.mango.dto.param.News;
import cn.cas.mango.service.News2.GetJson2;
import cn.cas.mango.service.News2.News2;
import cn.cas.mango.util.CheckParam;
import cn.cas.mango.util.StringUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TransNews {
  @Value("${elasticsearch.index}")
  private String writeIndex;

  @Autowired
  private ElasticsearchClient client;
  private final ObjectMapper objectMapper = new ObjectMapper();
  @Autowired
  private RequestService requestService;
  @Autowired
  private GetJson2 getJson2;

  private List<News2> cleanNews(List<News2> allNews) {
    List<News2> ret = new ArrayList<>();
    for (News2 news: allNews) {
      if (Strings.isNotBlank(news.getNewsContent()) && StringUtil.isChineseString(news.getNewsContent()) && news.getNewsKeywords() != null) {
        ret.add(news);
      }
    }
    return ret;
  }

  private List<Map<String, Object>> wrapKeywords(Object newsKeywords) {
    List<Map<String, Object>> ret = new ArrayList<>();
    List<String> processTemp = new ArrayList<>();
    if (newsKeywords instanceof String) {
      processTemp.add((String)newsKeywords);
    } else if (newsKeywords instanceof List) {
      List<String> temp = (List<String>) newsKeywords;
      processTemp.addAll(temp);
    } else {
      return ret;
    }
    int size = processTemp.size();
    double score = 1.0 / size;
    for (String keywords: processTemp) {
      Map<String, Object> map = new LinkedHashMap();
      map.put("name", keywords);
      map.put("score", score);
      ret.add(map);
    }
    return ret;
  }

  private News transNews2News(News2 news2) {
    String id = news2.getId();
    List<Map<String, Object>> newsKeywords = wrapKeywords(news2.getNewsKeywords());
    String content = news2.getNewsContent();
    String title = news2.getNewsTitle();
    String date = news2.getNewsPublicDate();
    News news = new News();
    news.setNm(id);
    news.setKeywords(newsKeywords);
    news.setSjmc(title);
    news.setSjms(content);
    news.setFssj(date);
    return news;
  }

  private boolean createIndex(String index, String type) {
    boolean exists;
    try {
      exists = client.indices().exists(x -> x.index(index)).value();
      if (exists) {
        client.indices().delete(new DeleteIndexRequest.Builder().index(index).build());
      }
      CreateIndexRequest createIndexRequest = CreateIndexRequest.of(x->x.index(index).mappings(m->m.withJson(new StringReader(Mapping.getMapping(type))))
          .settings(s->s.withJson(new StringReader(Mapping.setting()))));
      client.indices().create(createIndexRequest
//            .settings(s->s.withJson(new StringReader(Mapping.setting())))
      );
      return exists;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public JSONObject transNews(GlobalParam globalParam) {
    CheckParam.checkGlobal(globalParam);
    List<List<News2>> main;
    try {
      main = getJson2.main(globalParam);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    List<News2> allNews = new ArrayList<>();
    List<News2> finalAllNews = allNews;
    main.forEach(x-> finalAllNews.addAll(x));
    int beforeSize = allNews.size();
    allNews = cleanNews(allNews);
    int afterSize = allNews.size();
    log.info("before size: {}, after clean: {}", beforeSize, afterSize);
    List<News> allFinal = new ArrayList<>();
    allNews.forEach(x->allFinal.add(transNews2News(x)));
    List<BulkOperation> clusterOperations = allFinal.stream()
        .map(news ->
            BulkOperation.of(b -> b
                .index(y -> {
                      try {
                        return y
                            .index(writeIndex)
                            .document(JsonData.fromJson(objectMapper.writeValueAsString(news)));
                      } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                      }
                    }
                )
            )).collect(Collectors.toList());
    createIndex(writeIndex, "news");
    requestService.bulkWrite(clusterOperations);
    log.info("{} news finish write", afterSize);
    return new JSONObject();
  }
}
